#ifndef ARRAY2_H
#define ARRAY2_H

#include <cstdio>
#include <cassert>
#include "Vec.h"

#ifdef min
#undef min
#endif

#ifdef max
#undef max
#endif

//tohle soupnout nekam do utils
template<typename T> void swap(T& a,T& b)
{
  T tmp = a;
  a = b;
  b = tmp;
}
/*
int mod(const int a,const int n)
{
  const int r = a%n;
  return r<0 ? r+n : r;
}

struct WrapIndex
{
  const int& index;
  WrapIndex(const int& index_) : index(index_) {}
};

struct ClampIndex
{
  const int& index;
  ClampIndex(const int& index_) : index(index_) {}
};

struct MirrorIndex
{
  const int& index;
  MirrorIndex(const int& index_) : index(index_) {}
};

WrapIndex wrap(const int& index) 
{
  return WrapIndex(index);
}

ClampIndex clamp(const int& index) 
{
  return ClampIndex(index);
}

MirrorIndex mirror(const int& index) 
{
  return MirrorIndex(index);
}
*/

template<typename T> class Array2
{
public:  
  Array2() : s(0,0),d(0) {}

  Array2(int M,int N)
  {
    assert(M>0 && N>0);
    s = Vec2i(M,N);
    d = new T[s(0)*s(1)];
  }

  Array2(const Vec2i& s_)
  {
    /// XXX: predelat na neco jako assert(all(s>0));
    assert(s_(0)>0 && s_(1)>0);
    s = s_;
    d = new T[s(0)*s(1)];
  }
   
  Array2(const Array2& a)
  {    
    s = a.s;
    
    if (s(0)>0 && s(1)>0)
    {
      d = new T[s(0)*s(1)];
    
    // XXX: optimize this:
      for(int i=0;i<s(0)*s(1);i++) d[i] = a.d[i];
    }
    else
    {
      d = 0;
    }
  }

  Array2& operator=(const Array2<T>& a)
  {
    printf("slow copy\n");
    if (this!=&a)
    {
      if (s(0)==a.s(0) && s(1)==a.s(1))
      {
        // XXX: optimize this:
        for(int i=0;i<s(0)*s(1);i++) d[i] = a.d[i];
      }
      else
      {
        delete[] d;
        s = a.s;
    
        if (a.s(0)>0 && a.s(1)>0)
        {
          d = new T[s(0)*s(1)];  
          // XXX: optimize this:
          for(int i=0;i<s(0)*s(1);i++) d[i] = a.d[i];
        }
        else
        {
          d = 0;
        }
      }
    }
    else
    {
      printf("SELF ASSIGNMENT\n");
    }    
    
    return *this;
  }
      
  ~Array2()
  {
    delete[] d;
  }
  
  T& operator()(int i,int j)
  {
    assert(d!=0);
    assert(i>=0 && i<s(0) &&
           j>=0 && j<s(1));
    
    return d[i+j*s(0)];
  }
  
  const T& operator()(int i,int j) const
  {
    assert(d!=0);
    assert(i>=0 && i<s(0) &&
           j>=0 && j<s(1));
    
    return d[i+j*s(0)];
  }
        
  Vec2i size() const
  {
    return s;
  }
  
  int size(int dim) const
  {
    assert(dim==0 || dim==1);
    return size()(dim);
  }
  
  int width() const
  {
    return size(0);
  }
  
  int height() const
  {
    return size(1);
  }

  T* data()
  {
    return d;
  }

  const T* constData() const
  {
    return d;
  }
  
  const T* data() const
  {
    return d;
  }

  void clear()
  {
    delete[] d;
    s = Vec2i(0,0);
    d = 0;    
  }
  
  template<typename T> friend void swap(Array2<T>& a,Array2<T>& b);
  template<typename T> friend T min(const Array2<T>& a);
  template<typename T> friend T max(const Array2<T>& a);
    
private:  
  Vec2i  s;
  T*     d;
};
  
template<typename T> Vec2i size(const Array2<T>& a)
{
  return a.size();
}

template<typename T> void clear(Array2<T>& a)
{
  a.clear();
}

template<typename T> void swap(Array2<T>& a,Array2<T>& b)
{
  swap(a.s,b.s);    
  swap(a.d,b.d);
}

template<typename T> T min(const Array2<T>& a)
{
  assert(a.width()*a.height()>0);
  
  T minval = a.d[0];
  int n=(a.width()*a.height())-1;
  /// XXX: optimize?
  for(int i=1;i<n;i++) minval = min(minval,a.d[i]);

  return minval;
}

template<typename T> T max(const Array2<T>& a)
{
  assert(a.width()*a.height()>0);
  
  T maxval = a.d[0];
  int n=(a.width()*a.height())-1;
  /// XXX: optimize?
  for(int i=1;i<n;i++) maxval = max(maxval,a.d[i]);

  return maxval;
}
#endif